home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks '96 / Better Unmount / Source Code! / UnmountHackINIT.c < prev    next >
Encoding:
Text File  |  1996-06-22  |  9.5 KB  |  437 lines  |  [TEXT/CWIE]

  1. // Better Unmount
  2. //
  3. //    (C) 1996 Damon Cokenias All Rights Reserved
  4. //    cokenias@mtn-palace.com
  5. //
  6. #ifndef __powerc        /* (uses fake universal proc ptrs!) */
  7.  
  8. #include <A4Stuff.h>
  9.  
  10. #define                kErrorAlertID            6010
  11. #define                kErrorAlert2ID            6013
  12. #define                kConfirmAlertID            6012
  13.  
  14. #define                kProcResType            'Proc'
  15. #define                kProcResID                128
  16.  
  17. #define                kSettingsResType        'PREF'
  18. #define                kSettingsResID            128
  19.  
  20. #define                kFirstStrID                128
  21.  
  22. #define                kDisplayItemsButton        3
  23.  
  24. #define                kMaxVolumesToRemember    60
  25.  
  26. typedef pascal void (*InitMenusProc) (void);
  27. typedef pascal short (*AlertProc) (short alertID, ModalFilterUPP filterProc);
  28.  
  29. typedef pascal void (*DoDisplayItemsProc) (short vRefNum);
  30.  
  31. typedef struct {
  32.     short            silentUnmount;
  33. } SettingsRec, **SettingsHandle;
  34.  
  35. UniversalProcPtr    gUnmountProc = NULL;
  36. InitMenusProc        gInitMenusProc = NULL;
  37. AlertProc            gAlertProc = NULL;
  38. Boolean                gPatchesInstalled = false;
  39. Boolean                gTriggered = false;
  40. SettingsRec            gSettings;
  41. AliasHandle            gMeAlias = NULL;
  42. short                gVRefNums [kMaxVolumesToRemember];
  43. short                gNumVRefNums = 0;
  44.  
  45. asm void            MyUnmountVol (void);
  46. pascal void            MyInitMenus (void);
  47. pascal short        MyAlert (short alertID, ModalFilterUPP filterProc);
  48. Boolean                IsErrorMessage ();
  49. Boolean                IsConfirmMessage ();
  50. Boolean                StartsWith (const unsigned char* s, const unsigned char* prefix);
  51. Boolean                EndsWith (const unsigned char* s, const unsigned char* suffix);
  52. AliasHandle            MakeAliasToSelf ();
  53. short                DoErrorAlert (ModalFilterUPP filterProc, short whichAlert);
  54. void                DoDisplayItems (short vRefNum);
  55. pascal Boolean MyFilterProc (DialogPtr dlog, EventRecord* event, short* itemHit);
  56. void                SimulateClick (DialogPtr dlog, short item);
  57.  
  58. void                DoDisplayItems (short vRefNum)
  59. {
  60.     DoDisplayItemsProc*        proc;
  61.     
  62.     proc = (DoDisplayItemsProc*) Get1Resource (kProcResType, kProcResID);
  63.     if (!proc) {
  64.         SysBeep (1);
  65.         return;
  66.     } else {
  67.         HLockHi ((Handle) proc);
  68.         (*proc) (vRefNum);
  69.         ReleaseResource ((Handle) proc);
  70.     }
  71. }
  72.  
  73. enum {
  74.     kErrorPrefixStr,
  75.     kErrorPrefixStr2,
  76.     kErrorSuffixStr,
  77.     kConfirmPrefixStr,
  78.     kConfirmSuffixStr,
  79.     kNumStrings
  80. };
  81.  
  82. StringHandle        gStrings [kNumStrings];
  83.  
  84. void SimulateClick (DialogPtr dlog, short item)
  85. {
  86.     Rect                bounds;
  87.     short                itemType;
  88.     Handle                itemHandle;
  89.     long                ticks;
  90.  
  91.     GetDialogItem (dlog, kDisplayItemsButton, &itemType, &itemHandle, &bounds);
  92.     HiliteControl ((ControlHandle) itemHandle, 1);
  93.     Delay (8, &ticks);
  94.     HiliteControl ((ControlHandle) itemHandle, 0);
  95. }
  96.  
  97. pascal Boolean MyFilterProc (DialogPtr dlog, EventRecord* event, short* itemHit)
  98. {
  99.     Boolean                retVal = false;
  100.     
  101.     EnterCodeResource ();
  102.     
  103.     if (event -> what == keyDown || event -> what == autoKey) {
  104.         
  105.         if (event -> modifiers & cmdKey) {
  106.             
  107.             switch (event -> message & charCodeMask) {
  108.                 case 'd' :
  109.                     *itemHit = kDisplayItemsButton;
  110.                     SimulateClick (dlog, kDisplayItemsButton);
  111.                     retVal = true;
  112.                     break;
  113.             }
  114.         }
  115.     }
  116.  
  117. Exit:
  118.  
  119.     ExitCodeResource ();
  120.     return retVal;
  121. }
  122.  
  123. short DoErrorAlert (ModalFilterUPP filterProc, short whichAlert)
  124. {
  125.     short        oldResFile = CurResFile ();
  126.     short        itemHit;
  127.     FSSpec        mySpec;
  128.     short        myResFile;
  129.     OSErr        error;
  130.     Boolean        wasChanged;
  131.     Boolean        gotResources = false;
  132.     
  133.     /* find my resources */
  134.     error = ResolveAlias (nil, gMeAlias, &mySpec, &wasChanged);
  135.     if (error == noErr) {
  136.         myResFile = FSpOpenResFile (&mySpec, fsRdPerm);
  137.         if (myResFile > 0)
  138.             gotResources = true;
  139.     }
  140.         
  141.     if (gotResources) {
  142.         short        vRefNum, i;
  143.         
  144.         // itemHit = Alert (whichAlert, MyFilterProc);        not done w/ filter proc
  145.         itemHit = Alert (whichAlert, nil);
  146.         
  147.         if (gNumVRefNums) {
  148.             vRefNum = gVRefNums [0];
  149.             for (i = 1; i < gNumVRefNums; ++i)
  150.                 gVRefNums [i - 1] = gVRefNums [i];
  151.             --gNumVRefNums;
  152.  
  153.             if (itemHit == kDisplayItemsButton) {
  154.                 DoDisplayItems (vRefNum);
  155.                 itemHit = 1;
  156.             } else if (itemHit == 1) {
  157.                 gNumVRefNums = 0;
  158.             }
  159.         }
  160.     } else
  161.         itemHit = Alert (whichAlert, filterProc);
  162.  
  163.     
  164. Exit:
  165.     if (gotResources) {
  166.         CloseResFile (myResFile);
  167.         UseResFile (oldResFile);
  168.     }
  169.     
  170.     return itemHit;
  171. }
  172.  
  173. AliasHandle MakeAliasToSelf ()
  174. {
  175.     short            myRefNum = CurResFile ();
  176.     FCBPBRec        pb;
  177.     FSSpec            mySpec;
  178.     OSErr            error;
  179.     AliasHandle        a;
  180.     AliasHandle        sysHandle;
  181.     long            length;
  182.         
  183.     pb.ioFCBIndx = 0;
  184.     pb.ioNamePtr = mySpec.name;
  185.     pb.ioRefNum = myRefNum;
  186.  
  187.     if (PBGetFCBInfoSync (&pb))
  188.         return nil;
  189.     
  190.     mySpec.vRefNum = pb.ioFCBVRefNum;
  191.     mySpec.parID = pb.ioFCBParID;
  192.     
  193.     error = NewAlias (nil, &mySpec, &a);
  194.     if (error)
  195.         return nil;
  196.         
  197.     length = GetHandleSize ((Handle) a);
  198.     sysHandle = (AliasHandle) NewHandleSys (length);
  199.     if (!sysHandle) {
  200.         DisposeHandle ((Handle) a);
  201.         return nil;
  202.     }
  203.     
  204.     BlockMoveData (*a, *sysHandle, length);
  205.     DisposeHandle ((Handle) a);
  206.  
  207.     return sysHandle;
  208. }
  209.  
  210. Boolean StartsWith (const unsigned char* s, const unsigned char* prefix)
  211. {
  212.     int        i;
  213.     
  214.     if (s[0] < prefix [0])
  215.         return false;
  216.     
  217.     for (i = 1; i <= prefix [0]; ++i)
  218.         if (s [i] != prefix [i])
  219.             return false;
  220.     
  221.     return true;
  222. }
  223.  
  224. Boolean EndsWith (const unsigned char* s, const unsigned char* suffix)
  225. {
  226.     int        i, offset;
  227.     
  228.     offset = s [0] - suffix [0];
  229.     
  230.     if (offset < 0)
  231.         return false;
  232.     
  233.     for (i = 1; i <= suffix [0]; ++i)
  234.         if (s [i + offset] != suffix [i])
  235.             return false;
  236.     
  237.     return true;
  238. }
  239.  
  240. Boolean                IsErrorMessage ()
  241. {
  242.     StringHandle        s = LMGetDAStrings (0);
  243.     
  244.     if (s == nil)
  245.         return false;
  246.     
  247.     return ((StartsWith (*s, *gStrings [kErrorPrefixStr]) ||
  248.             StartsWith (*s, *gStrings [kErrorPrefixStr2])) &&
  249.             EndsWith (*s, *gStrings [kErrorSuffixStr]));
  250. }
  251.  
  252. Boolean                IsConfirmMessage ()
  253. {
  254.     StringHandle        s = LMGetDAStrings (0);
  255.     
  256.     if (s == nil)
  257.         return false;
  258.     
  259.     return (StartsWith (*s, *gStrings [kConfirmPrefixStr]) && EndsWith (*s, *gStrings [kConfirmSuffixStr]));
  260. }
  261.  
  262.  
  263. pascal short MyAlert (short alertID, ModalFilterUPP filterProc)
  264. {
  265.     short        itemHit;
  266.     
  267.     EnterCodeResource ();
  268.         
  269.     if (gSettings.silentUnmount && alertID == kConfirmAlertID && IsConfirmMessage ())
  270.         itemHit = 1;
  271.     else if (gTriggered == true && (alertID == kErrorAlertID || alertID == kErrorAlert2ID) &&
  272.             IsErrorMessage ()) {
  273.         gTriggered = false;
  274.         itemHit = DoErrorAlert (filterProc, alertID);
  275.     } else
  276.         itemHit = (gAlertProc) (alertID, filterProc);
  277.  
  278.     ExitCodeResource ();    
  279.     return itemHit;
  280. }
  281.  
  282. pascal void    MyInitMenus (void)
  283. {
  284.     long                    result;
  285.     ProcessSerialNumber        curPSN;
  286.     ProcessInfoRec            info;
  287.     
  288.     EnterCodeResource ();
  289.     
  290.     if (gPatchesInstalled)
  291.         goto Exit;
  292.             
  293.     /* is there a process manager ? */
  294.     if (Gestalt (gestaltOSAttr, &result))
  295.         goto Exit;
  296.  
  297.     if ((result & gestaltLaunchControl) == 0)
  298.         goto Exit;
  299.     
  300.     /* is this the Finder ? */
  301.  
  302.     curPSN.highLongOfPSN = 0;
  303.     curPSN.lowLongOfPSN = kCurrentProcess;
  304.     
  305.     info.processInfoLength = sizeof (ProcessInfoRec);
  306.     info.processName = nil;
  307.     info.processAppSpec = nil;
  308.     GetProcessInformation (&curPSN, &info);
  309.     
  310.     if (info.processSignature != 'MACS')
  311.         goto Exit;
  312.         
  313.     // Install here
  314.     gUnmountProc = GetOSTrapAddress (_UnmountVol);
  315.     SetOSTrapAddress ((UniversalProcPtr) MyUnmountVol, _UnmountVol);
  316.  
  317.     gAlertProc = (AlertProc) GetToolTrapAddress (_Alert);
  318.     SetToolTrapAddress ((UniversalProcPtr) MyAlert, _Alert);
  319.         
  320.     gPatchesInstalled = true;
  321.         
  322. Exit:
  323.  
  324.     (gInitMenusProc) ();
  325.  
  326.     ExitCodeResource ();
  327.     return;
  328. }
  329.  
  330.  
  331. /* called after a PBUnmountVol fails with fBsyErr */
  332. static
  333. void    myTailPatch (ParmBlkPtr unmountPB)
  334. {
  335.     EnterCodeResource ();
  336.  
  337.     if (unmountPB -> volumeParam.ioNamePtr)    /* we're not interested if volume reference by name */
  338.         goto Exit;
  339.  
  340.     if (gNumVRefNums == kMaxVolumesToRemember)
  341.         goto Exit;
  342.         
  343.     gVRefNums [gNumVRefNums++] = unmountPB -> volumeParam.ioVRefNum;
  344.             
  345.     gTriggered = true;
  346.  
  347. Exit:
  348.     ExitCodeResource ();
  349. }
  350.  
  351. asm void MyUnmountVol (void)
  352. {
  353.     /*
  354.     ** On entry, D1 contains the trap word used to call this routine
  355.     ** A0 contains a ParmBlkPtr
  356.     */
  357.     
  358.     BTST        #0x09, D1                    /* asynch version? */
  359.     BNE            exitToROM                    /* Yes, we don't deal with asynch calls */
  360.     
  361.     MOVE.L        A0, -(SP)                    /* remember param block ptr */
  362.     PEA            tailPatch                    /* push addr of our tail patch */
  363.  
  364. exitToROM:
  365.  
  366.     SUBQ.L        #4, SP                        /* reserve spot on stack */
  367.     MOVEM.L        D0-D2/A0-A2/A4, -(SP)        /* store regs */
  368.     LEA            SetCurrentA4, A0            /* setup A4 world */
  369.     JSR            (A0)
  370.     MOVE.L        gUnmountProc, 0x1C(sp)        /* put global in previously reserved space */
  371.     MOVEM.L        (SP)+, D0-D2/A0-A2/A4        /* restore regs (including A4) */
  372.     RTS                                        /* will jump to rom address and return to 'tailPatch' */
  373.  
  374. tailPatch:                                    /* we get here when rom RTSs */
  375.  
  376.     MOVE.L        (SP)+, A0                    /* recall param block ptr */
  377.     CMP.W        #fBsyErr, D0                /* are there files open? */
  378.     BEQ            filesStillOpen                /* yes, deal with it */
  379.     
  380. returnToCaller:
  381.     RTS                                        /* return to original caller */
  382.  
  383. filesStillOpen:
  384.     MOVEM.L        D0-D2/A0-A2, -(SP)            /* store regs */
  385.     MOVE.L        A0, -(SP)                    /* push parameter */
  386.     LEA            myTailPatch, A0                /* call C routine */
  387.     JSR            (A0)
  388.     ADDQ.L        #4, SP                        /* pop parameter */
  389.     MOVEM.L        (SP)+, D0-D2/A0-A2            /* restore regs */
  390.     BRA            returnToCaller                /* done */
  391. }
  392.  
  393.  
  394. void main ()
  395. {
  396.     Boolean            success = false;
  397.     Handle            self = Get1IndResource ('INIT', 1);
  398.     SettingsHandle    settings;
  399.     short            str;
  400.  
  401.     EnterCodeResource ();
  402.         
  403.     if (!self)
  404.         goto Exit;
  405.     
  406.     settings = (SettingsHandle) Get1Resource (kSettingsResType, kSettingsResID);
  407.     if (!settings)
  408.         goto Exit;
  409.         
  410.     gSettings = **settings;
  411.     
  412.     for (str = 0; str < kNumStrings; ++str) {
  413.         gStrings [str] = (StringHandle) Get1Resource ('STR ', kFirstStrID + str);
  414.         if (!gStrings [str])
  415.             goto Exit;
  416.     }
  417.     
  418.     gMeAlias = MakeAliasToSelf ();
  419.     if (!gMeAlias)
  420.         goto Exit;
  421.     
  422.     /* detach all important resources */
  423.     
  424.     DetachResource (self);
  425.     for (str = 0; str < kNumStrings; ++str)
  426.         DetachResource ((Handle) gStrings [str]);
  427.     
  428.         
  429.     gInitMenusProc = (InitMenusProc) GetToolTrapAddress (_InitMenus);
  430.     SetToolTrapAddress ((UniversalProcPtr) MyInitMenus, _InitMenus);
  431.  
  432. Exit:
  433.  
  434.     ExitCodeResource ();
  435. }
  436.  
  437. #endif